use std::{
    collections::HashMap,
    sync::{Arc, LazyLock},
};

use monitor::{CompleterPosFrom, CompleterSuggestion, Monitor, MonitorCommand};
use rcu::{call_rcu, RcuLock};

use crate::{DfsError, DfsFileDesc, DfsResult};

static FD: LazyLock<RcuLock<HashMap<u64, Arc<DfsFileDesc>>>> =
    LazyLock::new(|| RcuLock::new(HashMap::new()));

struct FdinfoCommand;

impl MonitorCommand for FdinfoCommand {
    fn mon_protocol_exec(&self, _: &monitor::Protocol) -> DfsResult<monitor::Protocol> {
        let id = do_show();
        let mut hash = HashMap::new();
        for i in id {
            hash.insert(i.clone(), i);
        }
        Ok(hash)
    }
    fn mon_readline_arg_type(&self) -> Option<&'static str> {
        Some("")
    }

    fn mon_readline_completer(&self, _: &[&str], _: bool) -> Option<CompleterSuggestion> {
        None
    }

    fn mon_readline_exec(
        &self,
        _: &HashMap<String, String>,
        printer: &monitor::MonitorPrinter,
    ) -> DfsResult<()> {
        let mut id = do_show();
        if id.is_empty() {
            return Ok(());
        }
        id.sort();
        printer.print_term(&id);

        Ok(())
    }

    fn mon_readline_help(&self) -> String {
        String::from("查看从命令行或者控制协议打开的文件")
    }

    fn mon_readline_long_help(&self) -> Option<String> {
        None
    }
}

pub(crate) fn monitor_command_fdinfo_register(mon: &Monitor) {
    mon.register_command("fdinfo", Arc::new(FdinfoCommand)).unwrap();
}

pub(crate) fn fdinfo_insert(fd: DfsFileDesc) -> DfsResult<()> {
    let mut lock = FD.write().unwrap();
    if lock.contains_key(&fd.id()) {
        return Err(DfsError::ExistObj);
    }
    lock.insert(fd.id(), Arc::new(fd));
    Ok(())
}

pub(crate) fn fdinfo_remove(id: u64) -> DfsResult<()> {
    let mut lock = FD.write().unwrap();
    let fd = lock.remove(&id).ok_or(DfsError::NoExistObj)?;
    call_rcu(move || drop(fd));
    Ok(())
}

fn do_show() -> Vec<String> {
    let lock = FD.read().unwrap();
    let mut v = Vec::new();
    for (_, fd) in lock.iter() {
        v.push(fd.id().to_string());
    }
    v
}

pub(crate) fn fdinfo_show() -> Vec<String> {
    do_show()
}

pub(crate) fn fdinfo_get(id: u64) -> DfsResult<Arc<DfsFileDesc>> {
    let lock = FD.read().unwrap();
    let file = lock.get(&id).ok_or(DfsError::NoExistObj)?;
    Ok(file.clone())
}

pub(crate) fn mon_fd_completer(args: &[&str], is_space: bool) -> Option<CompleterSuggestion> {
    let mut fd = fdinfo_show();
    fd.sort();
    if is_space {
        Some(CompleterSuggestion::create(CompleterPosFrom::Deafult, fd, true))
    } else {
        let arg = args.last().unwrap();
        let mut v = Vec::new();
        for i in fd {
            if i.starts_with(arg) {
                v.push(i);
            }
        }
        Some(CompleterSuggestion::create(CompleterPosFrom::Deafult, v, true))
    }
}
